// --------------------------------------------------------------------------------------------------------------------
// <copyright file="FileWatcher.cs" company="TechBits">
//   Copyright (c) TechBits. All rights reserved.
// </copyright>
// <summary>
//   Defines the FileWatcher type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace IntelliShortCut.Core
{
    using System;
    using System.IO;
    
    using Data;

    /// <summary>
    /// Enables for watching a file for changes.
    /// </summary>
    public class FileWatcher : IDisposable
    {
        #region "Private Members"

        /// <summary>
        /// Used to monitoring the file.
        /// </summary>
        private readonly FileSystemWatcher _fileWatcher;

        /// <summary>
        /// Saves the last position of the file cursor.
        /// </summary>
        private long _prevPosition;

        /// <summary>
        /// Saves the last data of the file.
        /// </summary>
        private string _prevData = string.Empty;

        /// <summary>
        /// The file stream.
        /// </summary>
        private FileStream _fileStream;

        /// <summary>
        /// The stream reader.
        /// </summary>
        private StreamReader _streamReader;

        #endregion

        #region "Constructor"

        /// <summary>
        /// Initializes a new instance of the <see cref="FileWatcher"/> class.
        /// </summary>
        public FileWatcher()
        {
            FullPath = string.Empty;
            _fileWatcher = new FileSystemWatcher();
        }

        #endregion

        #region "Events"

        /// <summary>
        /// Event that indicates the file has changed.
        /// </summary>
        public event EventHandler<FileWatcherEventArgs> OnFileChanged;

        #endregion

        #region "Public Members"

        /// <summary>
        /// Gets or sets the full path of the file.
        /// </summary>
        public string FullPath { get; set; }

        #endregion

        #region "Public Methods"

        /// <summary>
        /// Starts the file watcher.
        /// </summary>
        public void Start()
        {
            try
            {
                SetUpWatcher();

                if (_fileWatcher.EnableRaisingEvents == false)
                {
                    _fileStream = new FileStream(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                    _streamReader = new StreamReader(_fileStream);

                    _prevPosition = _fileStream.Length;
                    _fileWatcher.EnableRaisingEvents = true;
                }                
            }
            finally
            {                
                _streamReader.Close();
                _fileStream.Close();

                _fileWatcher.EnableRaisingEvents = true;
            }
        }

        /// <summary>
        /// Stops the file watcher.
        /// </summary>
        public void Stop()
        {
            _fileWatcher.EnableRaisingEvents = false;
        }

        /// <summary>
        /// Retrieve the data that has beed added recently to the file.
        /// </summary>
        /// <returns>
        /// Data added recently to the file.
        /// </returns>
        public string GetNewData()
        {
            return _prevData;
        }

        /// <summary>
        /// Retrieve the old data.
        /// </summary>
        /// <returns>
        /// Data that was on the file before the update.
        /// </returns>
        public string GetOldData()
        {
            string oldData;
            var fileStream = new FileStream(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            var streamReader = new StreamReader(fileStream);

            try
            {
                oldData = streamReader.ReadToEnd();
            }
            finally
            {
                streamReader.Close();
                fileStream.Close();                
            }
         
            return oldData;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            Stop();
            _fileWatcher.Dispose();
        }

        #endregion

        #region "Private Methods"

        /// <summary>
        /// Setup the file watcher.
        /// </summary>
        private void SetUpWatcher()
        {
            _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
            _fileWatcher.Path = Path.GetDirectoryName(FullPath);
            _fileWatcher.Filter = Path.GetFileName(FullPath);
            _fileWatcher.Changed += FileWatcherChanged;
            _fileWatcher.EnableRaisingEvents = false;
        }

        /// <summary>
        /// Event that indicates the file has changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event parameters.</param>
        private void FileWatcherChanged(object sender, FileSystemEventArgs e)
        {
            try
            {
                _fileStream = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                _streamReader = new StreamReader(_fileStream);

                if (_prevPosition < _fileStream.Length)
                {
                    _fileStream.Position = _prevPosition;
                    _prevData = _streamReader.ReadToEnd();
                    _prevPosition = _fileStream.Position;

                    if (OnFileChanged != null)
                    {
                        OnFileChanged(this, new FileWatcherEventArgs(FullPath, _prevData));
                    }
                }
                else
                {
                    _prevPosition = _fileStream.Length;
                }
            }
            finally
            {
                _streamReader.Close();
                _fileStream.Close();
            }
        }

        #endregion
    }
}